package com.supreme.commonmybatis.interceptor;

import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.supreme.common.constant.ConsoleColors;
import com.supreme.commonmybatis.annotation.RangeQuery;
import com.supreme.commonmybatis.annotation.SqlParam;
import com.supreme.commonmybatis.utils.ParseSqlUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Objects;

public class RangeQueryInterceptor implements InnerInterceptor {

    @Override
    public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        ConsoleColors.printf(ConsoleColors.YELLOW_BOLD_BRIGHT, "willDoQuery sql: %s\n", boundSql.getSql());
        return true;
    }

    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        String queryKey = null;
        String queryValues = null;
        String rangeQueryKeyName = RangeQuery.RangeQueryKey;
        RangeQuery rangeQueryAnno = AnnotationUtils.getAnnotation(parameter.getClass(), RangeQuery.class);
        if (Objects.nonNull(rangeQueryAnno)) {
            queryKey = rangeQueryAnno.queryKey();
            queryValues = rangeQueryAnno.queryValues();
            rangeQueryKeyName = rangeQueryAnno.rangeQueryKey();
        }

        String methodFullName = ms.getId();
        ConsoleColors.println(ConsoleColors.YELLOW_BOLD_BRIGHT, methodFullName);

        //测试用 Start
        Method mapperMethod = ParseSqlUtils.getReflectMethodByMFN(methodFullName, parameter);
        RangeQuery rangeQuery = AnnotationUtils.findAnnotation(mapperMethod, RangeQuery.class);
        ConsoleColors.printf(ConsoleColors.YELLOW_BOLD_BRIGHT,
                "rangeQuery queryKey: %s queryVal: %d\n", rangeQuery.queryKey(), rangeQuery.queryVal());

        final Parameter[] parameters = mapperMethod.getParameters();
        for (Parameter parameter1 : parameters) {
            final SqlParam sqlParam = AnnotationUtils.findAnnotation(parameter1, SqlParam.class);
            System.out.println("sqlParam: " + sqlParam);
        }

        try {
            final Object result = FieldUtils.readField(parameter, rangeQueryKeyName, true);
            System.out.println(rangeQueryKeyName + ": " + result);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        //测试用 End

        if (StringUtils.isNotBlank(queryKey) && StringUtils.isNotBlank(queryValues)) {
            String sql = boundSql.getSql();
            ConsoleColors.printf(ConsoleColors.BLUE_BOLD_BRIGHT, "originalSql: %s\n", sql);

            try {
                if (StringUtils.containsIgnoreCase(sql, "where")) {
                    sql += String.format(" and find_in_set(%s, '%s')", queryKey, queryValues);
                } else {
                    sql += String.format(" where find_in_set(%s, '%s')", queryKey, queryValues);
                }
                FieldUtils.writeDeclaredField(boundSql, "sql", sql, true);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

        ConsoleColors.printf(ConsoleColors.BLUE_BOLD_BRIGHT, "updatedSql: %s\n", boundSql.getSql());
    }

    @Override
    public boolean willDoUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
        return true;
    }

    @Override
    public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {

    }

    @Override
    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {

    }

    @Override
    public void beforeGetBoundSql(StatementHandler sh) {

    }
}
